package com.tongzhan.utils;

import org.springframework.beans.BeanUtils;
import org.springframework.util.Assert;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.BiFunction;
import java.util.stream.Collectors;

/**
 * bean 转换工具类
 *
 * @author Zephyr
 * @date 2019/11/8
 */
public class ModelUtils {


    public static <R, T> List<R> toVOList(Class<R> clazz, List<T> list, BiFunction<R, T, R> function) {
        return list.stream().map(c -> toVO(clazz, c, function)).collect(Collectors.toList());
    }

    /**
     * List<T> -> List<R>
     *
     * @param clazz 返回类型
     * @param list  待转换list
     * @param <R>   返回类型
     * @param <T>   原类型
     * @return List<R>
     */
    public static <R, T> List<R> toVOList(Class<R> clazz, List<T> list) {
        return list.stream().map(c -> toVO(clazz, c)).collect(Collectors.toList());
    }

    /**
     * <T> -> <R>
     *
     * @param clazz 返回类型
     * @param t     待转换对象
     * @param <R>   返回类型
     * @param <T>   原类型
     * @return R
     */
    public static <R, T> R toVO(Class<R> clazz, T t, BiFunction<R, T, R> function) {
        Assert.notNull(clazz);
        Assert.notNull(t);
        try {
            R r = clazz.newInstance();
            BeanUtils.copyProperties(t, r);
            if (function == null) {
                return r;
            }
            return function.apply(r, t);
        } catch (InstantiationException | IllegalAccessException e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }
    }

    /**
     * <T> -> <R>
     *
     * @param clazz 返回类型
     * @param t     待转换对象
     * @param <R>   返回类型
     * @param <T>   原类型
     * @return R
     */
    public static <R, T> R toVO(Class<R> clazz, T t) {
        return toVO(clazz, t, (R r1, T t1) -> {
            Class<?> rclass = r1.getClass();
            Method[] rmethods = rclass.getMethods();
            Method[] tmethods = t1.getClass().getMethods();
            Map<String, Object> tmap = new HashMap<>();
            for (Method m : tmethods) {
                String name = m.getName();
                if (name.startsWith("get")) {
                    try {
                        tmap.put(name.substring(3), m.invoke(t));
                    } catch (IllegalAccessException e) {
                        e.printStackTrace();
                    } catch (InvocationTargetException e) {
                        e.printStackTrace();
                    }
                }
            }
            for (Method m : rmethods) {
                String name = m.getName();
                String field = name.substring(3);
                if (name.startsWith("set") && tmap.get(field) != null) {
                    try {
                        Method getter = rclass.getMethod("get" + field);
                        if (getter.getReturnType().equals(String.class) && getter.invoke(r1) == null) {
                            m.invoke(r1, tmap.get(field).toString().trim());
                        }
                    } catch (NoSuchMethodException e) {
                        e.printStackTrace();
                    } catch (IllegalAccessException e) {
                        e.printStackTrace();
                    } catch (InvocationTargetException e) {
                        e.printStackTrace();
                    }
                }
            }
            for (Field r : rclass.getDeclaredFields()){
                try {
                    r.setAccessible(true);
                    Object o = r.get(r1);
                    if (o != null && r.getType().equals(String.class)) r.set(r1, o.toString().trim());
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
            return r1;
        });
    }
}
